﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

using King.Extensions;

namespace csg {
    
    public class Program {

        private static string[] FRAGMENTS = new[] { 
            "input-characters",
            "input-character",
            "new-line-character",

            "delimited-comment-text",
            "delimited-comment-section",
            "asterisks",
            "not-slash-or-asterisk",

            "available-identifier",
            "identifier-start-character",
            "identifier-part-characters",
            "identifier-part-character",
            "letter-character",

            "decimal-digit-character",
            "connecting-character",
            "combining-character",
            "formatting-character",

            "decimal-digits",
            "decimal-digit",
            "integer-type-suffix",
            "hex-digits",
            "hex-digit",

            "exponent-part",
            "sign",
            "real-type-suffix",

            "character",
            "regular-string-literal-characters",
            "regular-string-literal-character",

            "verbatim-string-literal-characters",
            "verbatim-string-literal-character",
            "single-verbatim-string-literal-character",
            "single-regular-string-literal-character",
            "quote-escape-sequence",

            "file-name-character",

            "single-character",
            "simple-escape-sequence",
            "hexadecimal-escape-sequence",
            "unicode-escape-sequence",
        };

        private static string[] TOKENS = new[] { 
            "identifier-or-keyword",
            //"identifier",
            "boolean-literal", 
            "decimal-integer-literal", 
            "hexadecimal-integer-literal",
            "real-literal",
            "character-literal",
            "regular-string-literal",
            "verbatim-string-literal",
            "null-literal",
            "skipped-characters",
                
            "single-line-comment",
            "delimited-comment",

            "whitespace",
            "new-line",

            //"not-number-sign",
            //"conditional-symbol",
            "file-name-characters"
        };

        public static string CleanLexical(string raw) {
            var result = raw;

            // remove extra spaces
            result = Regex.Replace(result, "[ \t]+", " ");

            // remove headers
            result = Regex.Replace(result, "^B[.][^\n]*\n", string.Empty, RegexOptions.Multiline);

            // remove available-identifier
            result = result.Replace("available-identifier\r\n", string.Empty);
            result = result.Replace("available-identifier:\r\nAn identifier-or-keyword that is not a keyword", string.Empty);
            result = result.Replace("not-number-sign:\r\nAny input-character except #", "");

            // delimited comment
            result = result.Replace("/* delimited-comment-textopt asterisks /", "'/*' .* '*/'");

            // unicode
            result = result.Replace("connecting-character: \r\nA Unicode character of the class Pc\r\nA unicode-escape-sequence representing a character of the class Pc\r\n", "");
            result = result.Replace("formatting-character: \r\nA Unicode character of the class Cf\r\nA unicode-escape-sequence representing a character of the class Cf\r\n", "");
            result = result.Replace("connecting-character\r\ncombining-character\r\nformatting-character\r\n", "combining-character\r\n");

            // boolean literal
            result = result.Replace("pp-primary-expression:\r\ntrue\r\nfalse\r\n", "pp-primary-expression:\r\nboolean-literal\r\n");

            // null literal
            result = result.Replace("new null object", "new object");

            // remove right-shifts
            result = result.Replace("right-shift:\r\n>|>\r\nright-shift-assignment:\r\n>|>=\r\n", "");
            result = result.Replace("=>", "=> >> >>=");

            // character classes
            result = result.Replace("Any character with Unicode class Zs", "{Zs}");
            result = result.Replace("A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl \r\n", "");
            result = result.Replace("A Unicode character of classes Mn or Mc \r\n", "");
            result = result.Replace("A Unicode character of the class Nd \r\n", "");
            result = result.Replace("A Unicode character of the class Pc\r\n", "");
            result = result.Replace("A Unicode character of the class Cf\r\n", "");

            result = result.Replace(@"A unicode-escape-sequence representing a character of classes Mn or Mc", "'\\\\'? {Mn}\r\n'\\\\'? {Mc}\r\n");
            result = result.Replace(@"A unicode-escape-sequence representing a character of the class Nd", "'\\\\'? {Nd}\r\n");
            result = result.Replace(@"A unicode-escape-sequence representing a character of the class Pc", "'\\\\'? {Pc}\r\n");
            result = result.Replace(@"A unicode-escape-sequence representing a character of the class Cf", "'\\\\'? {Cf}\r\n");
            result = result.Replace(@"A unicode-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl",
                "'\\\\'? {Lu}\r\n'\\\\'? {Ll}\r\n'\\\\'? {Lt}\r\n'\\\\'? {Lm}\r\n'\\\\'? {Lo}\r\n'\\\\'? {Nl}\r\n");

            // indent everything
            result = Regex.Replace(result, "^", "  ", RegexOptions.Multiline);

            // expand 'one of'
            result = Regex.Replace(result, @" one of\r\n(\s*(?<value>[^\s]+)(?<!\w:)(?=\s*))*",
                delegate(Match match) {
                    var captures = match.Groups["value"].Captures.Cast<Capture>().ToArray();
                    return captures.Select(o => "\r\n  " + o.Value).StringJoin();
                }, RegexOptions.Multiline);

            // unindent production names
            result = Regex.Replace(result, @"^\s*\w[^:\r\n]*:\s*\r\n", delegate(Match match) { return match.Value.TrimStart(); }, RegexOptions.Multiline);

            // indent first expansion
            result = Regex.Replace(result, @"(?<=\w):\s*\r\n  ", "\r\n  : ", RegexOptions.Multiline);

            // join expansions with |
            result = Regex.Replace(result, "^  (?!:)", "  | ", RegexOptions.Multiline);

            // break up productions
            result = Regex.Replace(result, @"(?<=\s)^(?=\w)", "\r\n", RegexOptions.Multiline);

            // unicode
            result = result.Replace("Carriage return character (U+000D) followed by line feed character (U+000A)", "'\\r' '\\n'");
            result = result.Replace("Carriage return character (U+000D)", "'\\r'");
            result = result.Replace("Line feed character (U+000A)", "'\\n'");
            result = result.Replace("Next line character (U+0085)", "'\\u0085'");
            result = result.Replace("Line separator character (U+2028)", "'\\u2028'");
            result = result.Replace("Paragraph separator character (U+2029)", "'\\u2029'");
            result = result.Replace(@"Horizontal tab character (U+0009)", "\\t");
            result = result.Replace(@"Vertical tab character (U+000B)", "\\v");
            result = result.Replace(@"Form feed character (U+000C)", "\\f");

            // except
            result = result.Replace("Any Unicode character except a new-line-character", "~new-line-character");
            result = result.Replace("Any Unicode character except / or *", "~('/' | '*')");
            result = result.Replace(@"Any character except ' (U+0027), \ (U+005C), and new-line-character", "~('\\\'' | '\\\\' | new-line-character)");
            result = result.Replace(@"Any character except "" (U+0022), \ (U+005C), and new-line-character", "~('\"' | '\\\\' | new-line-character)");
            result = result.Replace(@"any character except """, "~'\"'");
            result = result.Replace(@"_ (the underscore character U+005F)", "'_'");
            result = result.Replace(@"Any character except '\'' (U+0027), \ (U+005C), and new-line-character",
                @"~('\'' | '\\' | new-line-character)");
            result = result.Replace("Any input-character except \"", "~(NEW-LINE-CHARACTER | '\"')");
            result = result.Replace("Any input-character except #", "~(NEW-LINE-CHARACTER | '#')");
            result = result.Replace("Any identifier-or-keyword except true or false", "identifier-or-keyword");

            // optional
            result = Regex.Replace(result, @"\w*opt(?!\w-)", delegate(Match match) { return match.Value.ReplaceEnd("opt", "?"); });

            // escape unreserved antlr characters
            result = Regex.Replace(result, @"\s[^\s|:'()](?=\s)", delegate(Match match) { return " '" + match.Value.Trim() + "'"; });
            result = Regex.Replace(result, @"\s[^\w\s|:'().*]+(?=\s)", delegate(Match match) { return " '" + match.Value.Trim() + "'"; });
            result = result.Replace("  | |\r\n", "  | '|'\r\n");
            result = result.Replace("? )", "? ')'");
            result = result.Replace("  | (", "  | '('");
            result = result.Replace("  | )", "  | ')'");
            result = result.Replace("  | ||", "  | '||'");
            result = result.Replace("  | |=", "  | '|='");
            result = result.Replace("  :\r\n", "  | ':'\r\n");
            result = result.Replace("  ::\r\n", "  | '::'\r\n");
            result = result.Replace("  : \\'\r\n", "  : '\\''\r\n");
            result = result.Replace("' character '", "'\\'' character '\\''");
            result = Regex.Replace(result, @"(?<=\s)\\[0abfnrtxvuU](?=\s)", delegate(Match match) { return "'" + match.Value + "'"; });
            result = result.Replace(@"'\u'", @"'\\u'");
            result = result.Replace(@"'\U'", @"'\\U'");
            result = result.Replace(@"'\x'", @"'\\x'");
            result = result.Replace("  : '\\''\r\n  | '\\\"'\r\n  | '\\\\'\r\n  | '\\0'\r\n  | '\\a'\r\n  | '\\b'\r\n  | '\\f'\r\n  | '\\n'\r\n  | '\\r'\r\n  | '\\t'\r\n  | '\\v'\r\n",
                                    "  : '\\\\\\''\r\n  | '\\\\\"'\r\n  | '\\\\'\r\n  | '\\\\0'\r\n  | '\\\\a'\r\n  | '\\\\b'\r\n  | '\\\\f'\r\n  | '\\\\n'\r\n  | '\\\\r'\r\n  | '\\\\t'\r\n  | '\\\\v'\r\n");
            result = result.Replace(@"  | *=", @"  | '*='");

            result = result.Replace("'@' identifier-or-keyword", "'@'? identifier-or-keyword");

            // boolean literal
            result = result.Replace("  | true\r\n", string.Empty);
            result = result.Replace("  | false\r\n", string.Empty);
            result = result.Replace("  : true\r\n", "  : true\r\n  | false\r\n");
            result = result.Replace(
                "| keyword\r\n  | integer-literal\r\n  | real-literal\r\n  | character-literal\r\n  | string-literal",
                "| keyword\r\n  | literal");

            // partition productions, tokens, fragments
            var productions = Regex.Matches(result, @"^(?=\w)(?<name>((?!\r\n).)*)((?!\r\n\r\n).)*", RegexOptions.Multiline | RegexOptions.Singleline);
            var matches =
               (from o in productions.Cast<Match>()
                let name = o.Groups["name"].Value
                let isToken = TOKENS.Contains(name)
                let isFragment = FRAGMENTS.Contains(name)
                let isProduction = !isToken && !isFragment
                orderby isFragment, isToken, name == "skipped-characters", name == "identifier-or-keyword", isProduction
                where !new[] { 
                    "delimited-comment-text",
                    "delimited-comment-section",
                    "asterisks",
                    "not-slash-or-asterisk",
                }.Contains(name)
                select new {
                    Name = name,
                    IsFragment = isFragment,
                    isProduction = isProduction,
                    Value = o.Value
                }).ToArray();

            result = matches.StringJoin("\r\n  ;\r\n\r\n", o =>
                (o.IsFragment ? "fragment " : "") +
                //(o.isProduction ? "internal " : "") + 
                o.Value.Trim()) + "\r\n  ;";

            var names = matches.Select(o => o.Name).ToHashSet();
            names.Add("internal");
            names.Add("fragment");
            result = Regex.Replace(result, @"(?<=\s)\w[a-zA-Z-]*(?=[\s;])",
                delegate(Match match) {
                    var value = match.Value;
                    return names.Contains(value) ? value : "'" + value + "'";
                });
            result = result.Replace("  | internal", "  | 'internal'");

            // uppercase tokens
            foreach (var token in TOKENS.Union(FRAGMENTS))
                result = Regex.Replace(result, @"(?<=\s)[~]?" + token + @"[?;)*]*(?=\s)", 
                    delegate(Match match) {
                        var value = match.Value;
                        //if (FRAGMENTS.Contains(token))
                        //    value = value.Replace(token, "f-" + token);
                        return value.ToUpper(); 
                    });

            // unicode
            result = result.Replace("{Zs}", "' '");

            result = Regex.Replace(result, @"(?<=\w)[-](?=\w)", "_");

            result = result.Replace("{Lu}", "('a'..'z'|'A'..'Z')");
            result = result.Replace("{Ll}", "('a'..'z'|'A'..'Z')");
            result = result.Replace("{Lt}", "('a'..'z'|'A'..'Z')");
            result = result.Replace("{Lm}", "('a'..'z'|'A'..'Z')");
            result = result.Replace("{Lo}", "('a'..'z'|'A'..'Z')");
            result = result.Replace("{Nl}", "('a'..'z'|'A'..'Z')");

            result = result.Replace("{Mn}", "'_'");
            result = result.Replace("{Mc}", "'_'");

            result = result.Replace("{Nd}", "'0'..'9'");
            result = result.Replace("{Pc}", "''");
            result = result.Replace("{Cf}", "''");

            // left recursive
            result = result.Replace("input_section input_section_part", "input_section_part input_section");
            result = result.Replace("input_elements input_element", "input_element input_elements");
            result = result.Replace("pp_elif_sections pp_elif_section", "pp_elif_section pp_elif_sections");
            result = result.Replace("skipped_section skipped_section_part", "skipped_section_part skipped_section");
            result = result.Replace("FILE_NAME_CHARACTERS FILE_NAME_CHARACTER", "FILE_NAME_CHARACTER FILE_NAME_CHARACTERS");
            result = result.Replace("INPUT_CHARACTERS INPUT_CHARACTER", "INPUT_CHARACTER INPUT_CHARACTERS");
            result = result.Replace("DELIMITED_COMMENT_TEXT DELIMITED_COMMENT_SECTION", "DELIMITED_COMMENT_SECTION DELIMITED_COMMENT_TEXT");
            result = result.Replace("ASTERISKS '*'", " '*' ASTERISKS");
            result = result.Replace("IDENTIFIER_PART_CHARACTERS IDENTIFIER_PART_CHARACTER", "IDENTIFIER_PART_CHARACTER IDENTIFIER_PART_CHARACTERS");
            result = result.Replace("DECIMAL_DIGITS DECIMAL_DIGIT", "DECIMAL_DIGIT DECIMAL_DIGITS");
            result = result.Replace("HEX_DIGITS HEX_DIGIT", "HEX_DIGIT HEX_DIGITS");
            result = result.Replace("REGULAR_STRING_LITERAL_CHARACTERS REGULAR_STRING_LITERAL_CHARACTER", "REGULAR_STRING_LITERAL_CHARACTER REGULAR_STRING_LITERAL_CHARACTERS");
            result = result.Replace("VERBATIM_STRING_LITERAL_CHARACTERS VERBATIM_STRING_LITERAL_CHARACTER", "VERBATIM_STRING_LITERAL_CHARACTER VERBATIM_STRING_LITERAL_CHARACTERS");

            //result = result.Replace("pp_or_expression WHITESPACE? || WHITESPACE? pp_and_expression", "WHITESPACE? || WHITESPACE? pp_and_expression pp_or_expression");
            //result = result.Replace("pp_and_expression WHITESPACE? '&&' WHITESPACE? pp_equality_expression", "WHITESPACE? '&&' WHITESPACE? pp_equality_expression pp_and_expression");
            //result = result.Replace("pp_equality_expression WHITESPACE? '==' WHITESPACE? pp_unary_expression", "WHITESPACE? '==' WHITESPACE? pp_unary_expression pp_equality_expression");
            //result = result.Replace("pp_equality_expression WHITESPACE? '!=' WHITESPACE? pp_unary_expression", "WHITESPACE? '!=' WHITESPACE? pp_unary_expression pp_equality_expression");
            //result = result.Replace("warning_list WHITESPACE? ',' WHITESPACE? DECIMAL_DIGITS", "WHITESPACE? ',' WHITESPACE? DECIMAL_DIGITS warning_list");

            // plural
            result = result.Replace("skipped_section_part\r\n  : SKIPPED_CHARACTERS? NEW_LINE\r\n  | pp_directive\r\n  ;\r\n",
                "//skipped_section_part\r\n//  : SKIPPED_CHARACTERS? NEW_LINE\r\n//  | pp_directive\r\n//  ;\r\n");
            result = result.Replace("skipped_section\r\n  : skipped_section_part\r\n  | skipped_section_part skipped_section\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"^  [|] skipped_section(?!\w)", "//  | skipped_section_part+", RegexOptions.Multiline);
            result = result.Replace("input_section\r\n  : input_section_part\r\n  | input_section_part input_section\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)input_section(?!\w)", "input_section_part+");
            result = result.Replace("input_elements\r\n  : input_element\r\n  | input_element input_elements\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)input_elements(?!\w)", "input_element+");
            result = result.Replace("pp_elif_sections\r\n  : pp_elif_section\r\n  | pp_elif_section pp_elif_sections\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)pp_elif_sections(?!\w)", "pp_elif_section+");
            result = result.Replace("FILE_NAME_CHARACTERS\r\n  : FILE_NAME_CHARACTER\r\n  | FILE_NAME_CHARACTER FILE_NAME_CHARACTERS\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)FILE_NAME_CHARACTERS(?!\w)", "FILE_NAME_CHARACTER+");
            result = result.Replace("fragment INPUT_CHARACTERS\r\n  : INPUT_CHARACTER\r\n  | INPUT_CHARACTER INPUT_CHARACTERS\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)INPUT_CHARACTERS(?!\w)", "INPUT_CHARACTER+");
            result = result.Replace("fragment IDENTIFIER_PART_CHARACTERS\r\n  : IDENTIFIER_PART_CHARACTER\r\n  | IDENTIFIER_PART_CHARACTER IDENTIFIER_PART_CHARACTERS\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)IDENTIFIER_PART_CHARACTERS(?!\w)", "IDENTIFIER_PART_CHARACTER+");
            result = result.Replace("fragment DECIMAL_DIGITS\r\n  : DECIMAL_DIGIT\r\n  | DECIMAL_DIGIT DECIMAL_DIGITS\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)DECIMAL_DIGITS(?!\w)", "DECIMAL_DIGIT+");
            result = result.Replace("fragment HEX_DIGITS\r\n  : HEX_DIGIT\r\n  | HEX_DIGIT HEX_DIGITS\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)HEX_DIGITS(?!\w)", "HEX_DIGIT+");
            result = result.Replace("fragment REGULAR_STRING_LITERAL_CHARACTERS\r\n  : REGULAR_STRING_LITERAL_CHARACTER\r\n  | REGULAR_STRING_LITERAL_CHARACTER REGULAR_STRING_LITERAL_CHARACTERS\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)REGULAR_STRING_LITERAL_CHARACTERS(?!\w)", "REGULAR_STRING_LITERAL_CHARACTER+");
            result = result.Replace("fragment VERBATIM_STRING_LITERAL_CHARACTERS\r\n  : VERBATIM_STRING_LITERAL_CHARACTER\r\n  | VERBATIM_STRING_LITERAL_CHARACTER VERBATIM_STRING_LITERAL_CHARACTERS\r\n  ;\r\n\r\n", "");
            result = Regex.Replace(result, @"(?<!\w)VERBATIM_STRING_LITERAL_CHARACTERS(?!\w)", "VERBATIM_STRING_LITERAL_CHARACTER+");
            result = result.Replace("+?", "*");
            result = result.Replace("  : input_element* NEW_LINE", "  : input_element+ (NEW_LINE | EOF)");
            result = result.Replace("  : WHITESPACE\r\n  | comment", "  : comment");
            result = result.Replace("  : skipped_characters? NEW_LINE", "  : skipped_characters NEW_LINE");
            result = result.Replace("  : NEW_LINE\r\n  | WHITESPACE INPUT_CHARACTER* NEW_LINE", "  : INPUT_CHARACTER* NEW_LINE");

            result = result.Replace("HEX_DIGIT? HEX_DIGIT? HEX_DIGIT?", "((HEX_DIGIT? HEX_DIGIT)? HEX_DIGIT)?");
            result = result.Replace("  | '\\\\'? ('a'..'z'|'A'..'Z')\r\n", "");
            result = result.Replace("  | '\\\\'? '_'\r\n", "");

            // warning list
            result = result.Replace("  | warning_list WHITESPACE? ',' WHITESPACE? DECIMAL_DIGIT+\r\n", "");
            result = result.Replace("  : DECIMAL_DIGIT+\r\n", "  : DECIMAL_DIGIT+ (',' warning_list)?\r\n");

            // pp_epxression
            result = result.Replace("  : pp_and_expression", "  : pp_and_expression ('||' pp_or_expression)?");
            result = result.Replace("  | pp_or_expression WHITESPACE? || WHITESPACE? pp_and_expression\r\n", "");
            result = result.Replace("  : pp_equality_expression", "  : pp_equality_expression ('&&' pp_and_expression)?");
            result = result.Replace("  | pp_and_expression WHITESPACE? '&&' WHITESPACE? pp_equality_expression\r\n", "");
            result = result.Replace("  : pp_unary_expression", "  : pp_unary_expression (('!=' | '==') pp_equality_expression)?");
            result = result.Replace("  | pp_equality_expression WHITESPACE? '==' WHITESPACE? pp_unary_expression\r\n", "");
            result = result.Replace("  | pp_equality_expression WHITESPACE? '!=' WHITESPACE? pp_unary_expression\r\n", "");

            result = result.Replace(" WHITESPACE?", "");
            result = result.Replace(" WHITESPACE", "");
            result = result.Replace("  : NOT_NUMBER_SIGN INPUT_CHARACTER*", "  : NOT_NUMBER_SIGN INPUT_CHARACTER+");
            result = result.Replace("input\r\n", "public start\r\n");
            result = result.Replace("  : ' '\r\n  | '\\t'\r\n  | '\\v'\r\n  | '\\f'\r\n", "  : (' ' | '\\t' | '\\v' | '\\f')+ {skip();}\r\n");

            result = result.Replace("  : 'not_number_sign' INPUT_CHARACTER*", "  : ~(NEW_LINE_CHARACTER | '#') INPUT_CHARACTER*");
            result = result.Replace("line_indicator\r\n  : DECIMAL_DIGIT+ file_name \r\n  | DECIMAL_DIGIT+\r\n", 
                "line_indicator\r\n  : DECIMAL_DIGIT+ pp_file_name? \r\n");
            result = result.Replace("  : 'warning' warning_action\r\n  | 'warning' warning_action warning_list",
                "  : 'warning' warning_action warning_list?");
            result = result.Replace("SKIPPED_CHARACTERS\r\n  : ~(NEW_LINE_CHARACTER | '#') INPUT_CHARACTER*\r\n  ;\r\n",
                "//SKIPPED_CHARACTERS\r\n//  : ~(NEW_LINE_CHARACTER | '#') INPUT_CHARACTER*\r\n//  ;\r\n");

            // rename
            result = Regex.Replace(result, @"(?<!\w)conditional_symbol(?!\w)", "pp_conditional_symbol");
            result = Regex.Replace(result, @"(?<!\w)skipped_section_part(?!\w)", "pp_skipped_section_part");
            result = Regex.Replace(result, @"(?<!\w)conditional_section(?!\w)", "pp_conditional_section");
            result = Regex.Replace(result, @"(?<!\w)SKIPPED_CHARACTERS(?!\w)", "PP_SKIPPED_CHARACTERS");
            foreach (var fragment in FRAGMENTS) {
                var frag = fragment.ToUpper().Replace("-", "_");
                result = Regex.Replace(result, @"(?<!\w)" + frag + @"(?!\w)", "F_" + frag);
            }
            foreach (var pp in new[] { "pragma_body", "pragma_warning_body", "warning_action", "warning_list", "line_indicator", "file_name" })
                result = Regex.Replace(result, @"(?<!\w)" + pp + @"(?!\w)", "pp_" + pp);

            //result = Regex.Replace(result, "^(?<prefix>  ([|]|:))(?<line> .*)$", 
            //    delegate(Match match) {
            //        var value = match.Value;
            //        var prefix = match.Groups["prefix"].Value;
            //        var line = match.Groups["line"].Value;

            //        if (!value.Contains(@"\"))
            //            return value;
            //        return prefix + "//" + line; 
            //    }, RegexOptions.Multiline);

            return result;
        }

        public static void Main() {
         
            Environment.CurrentDirectory = Environment.CurrentDirectory + @"\..\..";
   
            using (var writer = new StreamWriter("Grammar.txt"))
                using (var reader = new StreamReader("GrammarRaw.txt"))
                    writer.Write(CleanLexical(reader.ReadToEnd()));
        }
    }    
}
